icon Fiches de Révision Détaillées : NSI Première

Thème 1 : Représentation des données

1.1 - Entier dans une base b

Principes

Nombre de bits 🖥️


1.2 - Binaire d'un entier relatif : Complément à 2

Principe

Le complément à 2 est la méthode standard pour représenter les entiers relatifs. Le bit de poids fort (le plus à gauche) indique le signe (0 pour positif, 1 for négatif).

Conversion d'un nombre négatif en complément à 2 (sur n bits)

  1. Prendre la valeur absolue du nombre.
  2. La convertir en binaire sur n bits (en complétant avec des 0 à gauche si besoin).
  3. Inverser tous les bits (les 0 deviennent des 1 et vice-versa). C'est le complément à 1.
  4. Ajouter 1 au résultat.

Exemple pour -5 sur 4 bits : 5 $\rightarrow$ 0101 $\rightarrow$ (inversion) 1010 $\rightarrow$ (ajout de 1) 1011. Donc $(-5)_{10} = (1011)_2$.

Conversion inverse (d'un nombre négatif en complément à 2 vers le décimal)

  1. Inverser tous les bits.
  2. Ajouter 1.
  3. Convertir le résultat binaire en décimal.
  4. Ajouter le signe "-".

Exemple pour 1011 sur 4 bits : 1011 $\rightarrow$ (inversion) 0100 $\rightarrow$ (ajout de 1) 0101. $(0101)_2 = 5_{10}$. Le nombre est donc -5.

Propriété : Sur n bits, on peut représenter les entiers de $−2^{n−1}$ à $2^{n−1}−1$.

1.3 - Nombres réels : Nombres à virgule flottante

Principe

La représentation des nombres réels en machine est souvent une approximation. Un nombre binaire à virgule se décompose avec des puissances de 2, y compris négatives pour la partie fractionnaire.

Problème de précision ⚠️

Certains nombres décimaux à développement fini n'ont pas de représentation binaire finie (ex: $0.1_{10} = 0.000110011..._2$). Cela mène à des erreurs d'arrondi inévitables.

>>> 0.1 + 0.2
0.30000000000000004

Recommandation : Pour comparer deux flottants `a` et `b`, on ne teste jamais `a == b`. On vérifie si leur différence est inférieure à une petite marge d'erreur (epsilon) : `abs(a - b) < epsilon`.

Virgule flottante

Un nombre est décomposé en 3 parties (norme IEEE-754) : signe (1 bit), exposant (décale la virgule) et mantisse (les chiffres significatifs). Cela permet de représenter des nombres très grands ou très petits.


1.4 - Expressions booléennes

Valeurs et Opérateurs

Propriétés (Algèbre de Boole)


1.5 - Encodage des caractères : ASCII, ISO, Unicode

Principe

Un ordinateur ne manipulant que des nombres, chaque caractère (lettre, symbole, etc.) doit être associé à un nombre unique. C'est le rôle de la table d'encodage.

Thème 2 : Types construits

2.1 & 2.3 - p-uplets (Tuples) et Dictionnaires

p-uplets (Tuples en Python)


Dictionnaires


2.2 - Tableaux (Listes en Python)

Caractéristiques

Création et Matrices

Thème 7 : Algorithmique

7.1 - Parcours Séquentiel

Le parcours séquentiel consiste à examiner chaque élément d'une structure de données (comme un tableau) l'un après l'autre, du début à la fin. C'est l'approche la plus simple mais elle est fondamentale.

Coût : Le coût est linéaire, noté $O(n)$, car dans le pire des cas, il faut parcourir les `n` éléments du tableau.

Recherche d'une occurrence

Recherche l'index de la première apparition d'une valeur dans un tableau.

def recherche_occurrence(tableau, valeur):
    for i in range(len(tableau)):
        if tableau[i] == valeur:
            return i  # Retourne l'index si la valeur est trouvée
    return -1  # Convention pour indiquer que la valeur n'est pas dans le tableau

Recherche d'un extremum (minimum et maximum)

def recherche_extremum(tableau):
    if not tableau:  # Cas d'un tableau vide
        return (None, None)
    
    minimum = tableau[0]
    maximum = tableau[0]

    for element in tableau:
        if element < minimum:
            minimum = element
        if element > maximum:
            maximum = element

    return (minimum, maximum)

Calcul d'une moyenne

def calcul_moyenne(tableau):
    if not tableau:
        return 0
        
    somme = 0
    for element in tableau:
        somme += element
    
    return somme / len(tableau)

7.2 - Tris par insertion et par sélection

Le but du tri est d'organiser les éléments d'un tableau dans un ordre spécifique (généralement croissant).

Coût : Ces deux algorithmes ont un coût quadratique, $O(n^2)$. Ils deviennent lents pour de grands tableaux.

Tri par sélection

Principe : On trouve le plus petit élément du tableau non trié et on l'échange avec l'élément au début de la partie non triée. On répète l'opération jusqu'à ce que tout le tableau soit trié.

def tri_selection(tableau):
    n = len(tableau)
    for i in range(n):
        # Trouver le minimum dans la partie non triée tableau[i..n-1]
        min_idx = i
        for j in range(i + 1, n):
            if tableau[j] < tableau[min_idx]:
                min_idx = j
        
        # Échanger le minimum trouvé avec le premier élément
        tableau[i], tableau[min_idx] = tableau[min_idx], tableau[i]
    return tableau

Tri par insertion

Principe : On parcourt le tableau et pour chaque élément, on l'insère à sa place correcte dans la partie déjà triée (qui se trouve à sa gauche).

def tri_insertion(tableau):
    for i in range(1, len(tableau)):
        cle = tableau[i]
        j = i - 1
        # Déplacer les éléments de tableau[0..i-1] qui sont plus grands que la clé
        # vers une position à droite de leur position actuelle
        while j >= 0 and cle < tableau[j]:
            tableau[j + 1] = tableau[j]
            j -= 1
        tableau[j + 1] = cle
    return tableau

7.3 - Algorithme des k plus proches voisins (k-NN)

Principe (Algorithme d'Apprentissage) 🧠

Pour classer un nouvel élément, on regarde ses k voisins les plus proches dans un jeu de données existant. Le nouvel élément se voit attribuer la classe la plus fréquente parmi ces k voisins.

  1. Choisir k : Le nombre de voisins à considérer (par exemple, k=3).
  2. Calculer les distances : Mesurer la distance (souvent euclidienne) entre le nouvel élément et tous les autres points.
  3. Trouver les voisins : Identifier les k points ayant les plus petites distances.
  4. Voter : Compter les classes des k voisins. La classe majoritaire l'emporte.

Programme Important

import math

def distance_euclidienne(point1, point2):
    # Suppose que les points sont des tuples/listes de coordonnées (x, y)
    return math.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2)

def k_plus_proches_voisins(donnees, point_a_classer, k):
    distances = []
    for categorie, points_categorie in donnees.items():
        for point in points_categorie:
            dist = distance_euclidienne(point, point_a_classer)
            distances.append((dist, categorie))

    # Trier les distances par ordre croissant
    distances.sort(key=lambda x: x[0])

    # Prendre les k premiers voisins
    voisins = distances[:k]

    # Compter les votes
    votes = {}
    for _, categorie in voisins:
        votes[categorie] = votes.get(categorie, 0) + 1
    
    # Retourner la catégorie avec le plus de votes
    return max(votes, key=votes.get)

7.4 - Recherche Dichotomique dans un tableau trié

Prérequis : Le tableau doit absolument être trié.

Principe

C'est une méthode de recherche très efficace qui consiste à diviser récursivement par deux l'intervalle de recherche. On compare l'élément cherché avec l'élément central du tableau. Si ce n'est pas le bon, on sait si l'élément se trouve dans la moitié gauche ou droite, et on élimine l'autre moitié.

Coût : Le coût est logarithmique, noté $O(\log n)$. C'est extrêmement rapide, même sur de très grands tableaux.

Programme Important

def recherche_dichotomique(tableau, valeur):
    debut = 0
    fin = len(tableau) - 1

    while debut <= fin:
        milieu = (debut + fin) // 2
        
        if tableau[milieu] == valeur:
            return milieu  # Trouvé !
        elif tableau[milieu] < valeur:
            debut = milieu + 1  # Chercher dans la moitié droite
        else: # tableau[milieu] > valeur
            fin = milieu - 1  # Chercher dans la moitié gauche
            
    return -1  # Non trouvé

7.5 - Algorithmes gloutons

Principe 💡

Un algorithme glouton (greedy algorithm) construit une solution à un problème étape par étape. À chaque étape, il fait le choix qui semble le meilleur localement, sans tenir compte des conséquences futures de ce choix.

Exemple : Rendu de monnaie 💶

Problème : Rendre une somme donnée avec le moins de pièces/billets possible. Stratégie gloutonne : Toujours donner la plus grande pièce/billet possible sans dépasser la somme restante.

def rendre_monnaie(montant_a_rendre, systeme_monetaire):
    # systeme_monetaire doit être trié par ordre décroissant
    # ex: [50, 20, 10, 5, 2, 1]
    
    rendu = {}
    for piece in systeme_monetaire:
        if montant_a_rendre >= piece:
            nombre_pieces = montant_a_rendre // piece
            rendu[piece] = nombre_pieces
            montant_a_rendre %= piece
            
    return rendu

# Pour le système euro, cette stratégie est optimale.
# Ce n'est pas vrai pour tous les systèmes monétaires !

Exemple : Problème du sac à dos (version simple)

Problème : Remplir un sac de capacité limitée avec des objets ayant chacun une valeur et un poids pour maximiser la valeur totale. Stratégie gloutonne (une parmi d'autres) : Trier les objets par rapport valeur/poids décroissant et prendre les meilleurs objets jusqu'à ce que le sac soit plein. Cette stratégie n'est pas toujours optimale pour le problème du sac à dos 0/1 (où on doit prendre ou laisser un objet entier).